/*
 * Synchronization primitives
 * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
 * $Revision: 1.13 $
 * 
 * This is free software.  You are permitted to use,
 * redistribute, and modify it as specified in the file "COPYING".
 */

#include <geekos/kthread.h>
#include <geekos/int.h>
#include <geekos/kassert.h>
#include <geekos/screen.h>
#include <geekos/synch.h>
#include <geekos/bitset.h>
#include <geekos/string.h>

/*
 * NOTES:
 * - The GeekOS mutex and condition variable APIs are based on those
 *   in pthreads.
 * - Unlike disabling interrupts, mutexes offer NO protection against
 *   concurrent execution of interrupt handlers.  Mutexes and
 *   condition variables should only be used from kernel threads,
 *   with interrupts enabled.
 */

/*
 * Semaphores
 */
#define MAX_SEMAPHORES 20   /*Cantidad Maxima de semaforos en el sistema*/

struct Semaphore{
    char name[MAX_NAME_SEMAPHORES];
    int value;
    int refcount;
    struct Mutex mutex;
    struct Condition condition;
};
static struct Semaphore semaphores[MAX_SEMAPHORES]; /*Almacena todos los semaforos del sistema*/
struct Mutex mutex_crear_semaforo;  /*Usado para sincronizar la creacion de semaforos*/
static tlocal_key_t keySemaphore;   /*Posicion de tlocalData donde se almacenara informacion de los semaforos que posee cada thread*/
/* ----------------------------------------------------------------------
 * Private functions
 * ---------------------------------------------------------------------- */

/*
 * The mutex is currently locked.
 * Atomically reenable preemption and wait in the
 * mutex's wait queue.
 */
static void Mutex_Wait(struct Mutex *mutex)
{
    KASSERT(mutex->state == MUTEX_LOCKED);
    KASSERT(g_preemptionDisabled);

    Disable_Interrupts();
    g_preemptionDisabled = false;
    Wait(&mutex->waitQueue);
    g_preemptionDisabled = true;
    Enable_Interrupts();
}

/*
 * Lock given mutex.
 * Preemption must be disabled.
 */
static __inline__ void Mutex_Lock_Imp(struct Mutex* mutex)
{
    KASSERT(g_preemptionDisabled);

    /* Make sure we're not already holding the mutex */
    KASSERT(!IS_HELD(mutex));

    /* Wait until the mutex is in an unlocked state */
    while (mutex->state == MUTEX_LOCKED) {
	Mutex_Wait(mutex);
    }

    /* Now it's ours! */
    mutex->state = MUTEX_LOCKED;
    mutex->owner = g_currentThread;
}

/*
 * Unlock given mutex.
 * Preemption must be disabled.
 */
static __inline__ void Mutex_Unlock_Imp(struct Mutex* mutex)
{
    KASSERT(g_preemptionDisabled);

    /* Make sure mutex was actually acquired by this thread. */
    KASSERT(IS_HELD(mutex));

    /* Unlock the mutex. */
    mutex->state = MUTEX_UNLOCKED;
    mutex->owner = 0;

    /*
     * If there are threads waiting to acquire the mutex,
     * wake one of them up.  Note that it is legal to inspect
     * the queue with interrupts enabled because preemption
     * is disabled, and therefore we know that no thread can
     * concurrently add itself to the queue.
     */
    if (!Is_Thread_Queue_Empty(&mutex->waitQueue)) {
	Disable_Interrupts();
	Wake_Up_One(&mutex->waitQueue);
	Enable_Interrupts();
    }
}

/*
 * Destructor de todos los semaforos que tiene el Thread. 
 * Ademas destruye la estructura que se ua para almacenar la 
 * informacion de los semaforos que posee el thread.
 * Esta funcion se invoca automaticamente cuando un thread termina.
 */
void Destructor_TLocal_Semaphores(void* bitset){
    int i = 0;
    for(i=0;i<MAX_SEMAPHORES;i++){
        if(Is_Bit_Set(bitset, i)){
            DestroySemaphore(i);
        }        
    }
    Destroy_Bit_Set(bitset);
}

/* ----------------------------------------------------------------------
 * Public functions
 * ---------------------------------------------------------------------- */

/*
 * Initialize given mutex.
 */
void Mutex_Init(struct Mutex* mutex)
{
    mutex->state = MUTEX_UNLOCKED;
    mutex->owner = 0;
    Clear_Thread_Queue(&mutex->waitQueue);
}

/*
 * Lock given mutex.
 */
void Mutex_Lock(struct Mutex* mutex)
{
    KASSERT(Interrupts_Enabled());

    g_preemptionDisabled = true;
    Mutex_Lock_Imp(mutex);
    g_preemptionDisabled = false;
}

/*
 * Unlock given mutex.
 */
void Mutex_Unlock(struct Mutex* mutex)
{
    KASSERT(Interrupts_Enabled());

    g_preemptionDisabled = true;
    Mutex_Unlock_Imp(mutex);
    g_preemptionDisabled = false;
}

/*
 * Initialize given condition.
 */
void Cond_Init(struct Condition* cond)
{
    Clear_Thread_Queue(&cond->waitQueue);
}

/*
 * Wait on given condition (protected by given mutex).
 */
void Cond_Wait(struct Condition* cond, struct Mutex* mutex)
{
    KASSERT(Interrupts_Enabled());

    /* Ensure mutex is held. */
    KASSERT(IS_HELD(mutex));

    /* Turn off scheduling. */
    g_preemptionDisabled = true;

    /*
     * Release the mutex, but leave preemption disabled.
     * No other threads will be able to run before this thread
     * is able to wait.  Therefore, this thread will not
     * miss the eventual notification on the condition.
     */
    Mutex_Unlock_Imp(mutex);

    /*
     * Atomically reenable preemption and wait in the condition wait queue.
     * Other threads can run while this thread is waiting,
     * and eventually one of them will call Cond_Signal() or Cond_Broadcast()
     * to wake up this thread.
     * On wakeup, disable preemption again.
     */
    Disable_Interrupts();
    g_preemptionDisabled = false;
    Wait(&cond->waitQueue);
    g_preemptionDisabled = true;
    Enable_Interrupts();

    /* Reacquire the mutex. */
    Mutex_Lock_Imp(mutex);

    /* Turn scheduling back on. */
    g_preemptionDisabled = false;
}

/*
 * Wake up one thread waiting on the given condition.
 * The mutex guarding the condition should be held!
 */
void Cond_Signal(struct Condition* cond)
{
    KASSERT(Interrupts_Enabled());
    Disable_Interrupts();  /* prevent scheduling */
    Wake_Up_One(&cond->waitQueue);
    Enable_Interrupts();  /* resume scheduling */
}

/*
 * Wake up all threads waiting on the given condition.
 * The mutex guarding the condition should be held!
 */
void Cond_Broadcast(struct Condition* cond)
{
    KASSERT(Interrupts_Enabled());
    Disable_Interrupts();  /* prevent scheduling */
    Wake_Up(&cond->waitQueue);
    Enable_Interrupts();  /* resume scheduling */
}

/* ----------------------------------------------------------------------
 * Funciones de semaforos
 * ---------------------------------------------------------------------- */

/*
 * Inicializa la variable keySemaphore, registra el destructor de todos los
 * semaforos e inicializa el mutex para sincronizar la creacion de semaforos.
 * Esta funcion es llamada en el main.
 */
void Init_Semaphores(){
    int resultado;
    resultado = Tlocal_Create(&keySemaphore, Destructor_TLocal_Semaphores);
    Mutex_Init(&mutex_crear_semaforo);
    KASSERT(resultado == 0);       
}

/*
 * Funcion que crea un semaforo. Si el semaforo ya esta creado, lo registra
 * en el thread y devuelve su identificador.
 * Retorna: el id del semaforo si lo encontro o lo pudo crear. 
 * -1 si no hay mas lugar en el sistema para crear un nuevo semaforo.
 */
int CreateSemaphore(char name[MAX_NAME_SEMAPHORES], int value){
    void* bitset = Tlocal_Get(keySemaphore);
    int i = 0, semLibre = -1;
    /*Crea la estructura usada para registrar los semaforos en el thread*/
    if(bitset == 0){
        bitset = Create_Bit_Set(MAX_SEMAPHORES);
        Tlocal_Put(keySemaphore, bitset);
    }
    Mutex_Lock(&mutex_crear_semaforo);
    /*Busca si el semaforo esta creado. Rcuerda una posicion libre por si tiene que crearlo.*/
    for(i=0;i<MAX_SEMAPHORES;i++){
        if(strcmp(semaphores[i].name,name) == 0){
            semaphores[i].refcount++;
            Set_Bit(bitset, i);
            Mutex_Unlock(&mutex_crear_semaforo);
            return i;
        }else if(semaphores[i].refcount == 0){
            semLibre = i;
        }
    }
    /*Crea un nuevo semaforo*/
    if(semLibre != -1){
        memset(semaphores[semLibre].name,'\0',MAX_NAME_SEMAPHORES);
        strcpy(semaphores[semLibre].name, name);
        semaphores[semLibre].value = value;
        semaphores[semLibre].refcount = 1;
        Set_Bit(bitset, semLibre);
        Mutex_Init(&(semaphores[semLibre].mutex));
        Cond_Init(&(semaphores[semLibre].condition));
        Mutex_Unlock(&mutex_crear_semaforo);
        return semLibre;
    }  
    Mutex_Unlock(&mutex_crear_semaforo);
    return -1;  /*Error, falta memoria para crear semaforos*/
}
/*
 * Decrementa el valor del semaforo pasado como parametro.
 * Retorna: 0 si tuvo exito, -1 en caso que el thread no posea el semaforo idSemaphore
 */ 
int P(int idSemaphore){
    void* bitset = Tlocal_Get(keySemaphore);
    if (bitset==0)  /*Controla que el thread tenga semaforos registrados*/
        return -1;
    if(Is_Bit_Set(bitset, idSemaphore)){    /*Controla que el thread tenga el semaforo idSemaphore registrado*/
        Mutex_Lock(&semaphores[idSemaphore].mutex);
        if (semaphores[idSemaphore].value == 0)
            Cond_Wait(&semaphores[idSemaphore].condition, &semaphores[idSemaphore].mutex);
        else 
            semaphores[idSemaphore].value--;
        Mutex_Unlock(&semaphores[idSemaphore].mutex);
        return 0;
    }else
        return -1;
}

/*
 * Incrementa el valor del semaforo pasado como parametro.
 * Retorna: 0 si tuvo exito, -1 en caso que el thread no posea el semaforo idSemaphore
 */ 
int V(int idSemaphore){
    void* bitset = Tlocal_Get(keySemaphore);
    if (bitset==0)  /*Controla que el thread tenga semaforos registrados*/
        return -1;
    if(Is_Bit_Set(bitset, idSemaphore)){    /*Controla que el thread tenga el semaforo idSemaphore registrado*/
        Mutex_Lock(&semaphores[idSemaphore].mutex); 
        if (Is_Thread_Queue_Empty(&semaphores[idSemaphore].condition.waitQueue))    /*si no hay procesos_bloqueados*/
            (semaphores[idSemaphore].value)++;
        else 
            Cond_Signal(&semaphores[idSemaphore].condition);
        Mutex_Unlock(&semaphores[idSemaphore].mutex); 
        return 0;   
    }else
        return -1;
}

/*
 * Destruye el semaforo pasado como parametro.
 * Retorna: 0 si tuvo exito, -1 en caso que el thread no posea el semaforo idSemaphore.
 */ 
int DestroySemaphore(int idSemaphore){
    void* bitset = Tlocal_Get(keySemaphore);
    if (bitset==0)  /*Controla que el thread tenga semaforos registrados*/
        return -1;
    if(Is_Bit_Set(bitset, idSemaphore)){    /*Controla que el thread tenga el semaforo idSemaphore registrado*/
        Mutex_Lock(&semaphores[idSemaphore].mutex);
        if(semaphores[idSemaphore].refcount <= 0){  /*Controla que no elimine un semaforo que no existe*/
            Mutex_Unlock(&semaphores[idSemaphore].mutex);
            return -1;
        } else{
            (semaphores[idSemaphore].refcount)--;
            if((semaphores[idSemaphore].refcount) == 0){    /*Destruye realmente el semaforo si no esta siendo usado por otro thread*/
                memset(semaphores[idSemaphore].name, '\0',MAX_NAME_SEMAPHORES);
            }
            Clear_Bit(bitset, idSemaphore);
            Mutex_Unlock(&semaphores[idSemaphore].mutex);  
            return 0;
        }
    }
    return -1;  /*Si no posee el semaforo idSemaphore*/
}
